package com.xy.servicemanage.action;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.support.incrementer.OracleSequenceMaxValueIncrementer;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import com.google.gson.GsonBuilder;
import com.xy.common.util.Configure;
import com.xy.manage.model.Trequest;
import com.xy.manage.service.TrequestService;
import com.xy.servicemanage.model.ServiceSetting;
import com.xy.servicemanage.model.Treqparams;
import com.xy.servicemanage.model.Tservicecustomer;
import com.xy.servicemanage.model.Tservices;
import com.xy.servicemanage.request.RequestHelper;
import com.xy.servicemanage.utils.SerializeUtil;

@Controller
public class TservicesAction {
	private static final Logger log = LoggerFactory
			.getLogger(TservicesAction.class);
	public static Queue<Number> queueQuery = new LinkedBlockingQueue<Number>();
	public static Queue<Number> notifyQuery = new LinkedBlockingQueue<Number>();
	static {
		new Thread(new QueryThread()).start();
		new Thread(new NotifyThread()).start();
		new Thread(new LockTimeoutThread()).start();
	}
	private static List<String> sessionList = new ArrayList<String>();

	public TservicesAction() {
	}

	/**
	 * 服务定义新增页面
	 * 
	 * @param request
	 * @param model
	 * @return
	 */
	@RequestMapping("/request")
	public String request(HttpServletRequest request,
			HttpServletResponse response, ModelMap model) throws Exception {
		Map<String, Object> vmap = null;
		boolean isMultipart = ServletFileUpload.isMultipartContent(request);
		if (isMultipart) {
			MultipartHttpServletRequest mh = (MultipartHttpServletRequest) request;
			vmap = getReqValues(mh);
			Map<String,MultipartFile> m=mh.getFileMap();
			Set<String> set=m.keySet();
			for(String name:set){
				MultipartFile mf= 	m.get(name);
				vmap.put(name, mf.getBytes());
			}

		} else {
			vmap = getReqValues(request);
		}

		Object sid = vmap.get(serviceId) ;
		Object cid = vmap.get(custId);
		if (sid == null || sid.toString().trim().equals("")) {
			return getError("10001", "serviceId is null", response);
		}
		if (cid == null || cid.toString().trim().equals("")) {
			return getError("10002", "custId is null", response);
		}

		Tservicecustomer cust = ServiceSetting.getInstance()
				.getServicecustomerByCustId(cid.toString());
		if (cust == null) {
			return getError("10003", "customer not valid", response);
		}

		Tservices service = ServiceSetting.getInstance().getServiceByServiceId(
				sid.toString());
		if (service == null) {
			return getError("10004", "service not found", response);
		}
		if (cust.getServiceList().indexOf(sid.toString()) == -1) {
			return getError("10004", "service not allow", response);
		}
		if (!validRequest(vmap, service)) {
			return getError("10005", "request parameter not validate", response);
		}
		if (!cust.getCustId().equals("100")&&!validSign(vmap, cust)) {
			return getError("10006", "signmsg not validate", response);
		}

		if (!cust.getCustId().equals("100")&&!Configure.getProperty("serviceType").equals("dev")) {
			Object signmsg = vmap.get("signmsg");
			if (sessionList.contains(signmsg)) {
				return getError("10009", "Do not submit the same request!",response);
			}
			sessionList.add(signmsg.toString());
			if (sessionList.size() > 10000) {
				sessionList.remove(0);
			}
		}
		
		if ("1011,1012,,1013,1014,1015,1016,1017,1018,1037,1038".indexOf(sid.toString()) != -1) {
			System.out.println("++++++++++++使用外置服务器数据++++++++++++");
			Map vmap1 = vmap;
			String res = RequestHelper.processRequest(service, vmap1);
//			res.replace("/upload", Configure.getProperty("servicePath")+"/upload");
			sendBack(response, res);
			return null;
		}

		byte[] b = SerializeUtil.serializeObject(vmap);

		long rid = seqDispGenerator.nextLongValue();
		// insert new request to DB
		Trequest t = new Trequest();
		t.setRequest(b);
		t.setRid(rid);
		trequestService.save(t);
		log.debug("Request is  in waiting:" + rid);
		// waiting
		Object lock = getLockObject(rid);
		try {
			synchronized (lock) {
				lock.wait();
			}

		} catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		log.debug("Request has been notified:" + rid);

		if (System.currentTimeMillis() - ((LockObject) lock).getTime() > 30000) {
			log.debug("Request process Timeout:" + rid);
			lockMap.remove(rid);
			trequestService.remove(rid);
			return getError("10009", "request processs timeout", response);
		}
		// get result from DB using rid
		t = trequestService.get(rid);

		// maybe record not update，so wait
		int i = 0;
		while (t.getResponse() == null && i < 60) {
			log.debug("response not update, waiting:" + i);
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			i++;
			t = trequestService.get(rid);
		}

		if (i == 60) {
			log.debug("response not update, get away:" + i);
			return getError("10008", "system error", response);
		}

		String res = "";

		try {
			res = (String) SerializeUtil.deserializeObject(t.getResponse());
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		sendBack(response, res);
		lockMap.remove(rid);
		trequestService.remove(rid);
		return null;
	}
private void sendBack(HttpServletResponse response, String res){
	log.debug("response is:" + res);
	try {

		response.setCharacterEncoding("GBK");
		response.getWriter().print(res);
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}

}
	@RequestMapping("/notify")
	public String notify(HttpServletRequest request,
			HttpServletResponse response, ModelMap model, long rid) {
		log.debug("Local scan thread notify comming:" + rid);
		Object lock = getLockObject(rid);
		synchronized (lock) {
			lock.notify();
		}
		return null;
	}

	@RequestMapping("/toQuery")
	public String toQuery(HttpServletRequest request,
			HttpServletResponse response, ModelMap model, long rid) {

		queueQuery.add(rid);
		log.debug("DB trigger query comming:" + rid);
		return null;
	}

	@RequestMapping("/toNotify")
	public String toNotify(HttpServletRequest request,
			HttpServletResponse response, ModelMap model, long rid) {
		notifyQuery.add(rid);
		log.debug("DB trigger notify comming:" + rid);
		return null;
	}

	@RequestMapping("/query")
	public String query(HttpServletRequest request,
			HttpServletResponse response, ModelMap model, long rid) {
		// get query value map from DB by rid
		log.debug("requestid comming:" + rid);
		Trequest t = trequestService.get(rid);
		int i = 0;
		// maybe record is null，so wait
		while (t == null && i < 60) {
			log.debug("query record is null,waiting:" + i);
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			i++;
			t = trequestService.get(rid);
		}
		if (i == 60) {
			log.debug("query record is null, get away:" + i);
			return null;
		}
		byte[] b = t.getRequest();

		Map<String, String> vmap = (Map<String, String>) SerializeUtil
				.deserializeObject(b);

		String sid = vmap.get(serviceId);
		Tservices service = ServiceSetting.getInstance().getServiceByServiceId(
				sid);

		// update queryReulst to DB by rid
		String queryReulst = RequestHelper.processRequest(service, vmap);
		log.debug("query result is:" + queryReulst);

		t = trequestService.get(rid);
		if (t == null) {
			log.error("Query time out:" + rid + "--->" + queryReulst);
			return null;
		}
		try {
			// SerializeUtil.serializeObject(queryReulst);
			t.setResponse(SerializeUtil.serializeObject(queryReulst));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		trequestService.save(t);

		return null;
	}

	private static boolean validRequest(Map<String, Object> values,
			Tservices service) {
		List<Treqparams> list = ServiceSetting.getInstance().getReqparamBySid(
				service.getSid());
		if (list == null)
			return true;
		for (Treqparams r : list) {
			if ("1".equals(r.getIsMustin())) {
				Object v = values.get(r.getReqid());
				if (v == null || "".equals(v)) {
					log.info("parameter can not be null:"
							+ service.getServiceId() + "->" + r.getReqid());
					return false;
				}
				if (r.getReqlen() != null && !"".equals(r.getReqlen())) {
					int l = Integer.valueOf(r.getReqlen());
					if (l < v.toString().length()) {
						log.info("parameter max length is:" + r.getReqlen()
								+ " , and input length is:"
								+ service.getServiceId() + "->" + r.getReqid());
						return false;
					}
				}
			}
		}
		return true;
	}

	private static boolean validSign(Map<String, Object> values,
			Tservicecustomer cust) {
		if (Configure.getProperty("serviceType").equals("dev"))
			return true;
		Object sign = values.get(signMsg) ;
		if (sign == null || "".equals(sign)) {
			return false;
		}

		Object sendtime = values.get(sendTime) ;
		String key = cust.getPrikey();
		String t = key + sendtime;
		String s = crypt(t);
		if (!sign.equals(s)) {
			log.info("Sign msg not valid--> source:" + sign + ", md5:" + s
					+ "(" + t + ")");
			return false;
		}
		return true;

	}

	public static String crypt(String str) {
		if (str == null || str.length() == 0) {
			throw new IllegalArgumentException(
					"String to encript cannot be null or zero length");
		}

		StringBuffer hexString = new StringBuffer();

		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			md.update(str.getBytes());
			byte[] hash = md.digest();

			for (int i = 0; i < hash.length; i++) {
				if ((0xff & hash[i]) < 0x10) {
					hexString.append("0"
							+ Integer.toHexString((0xFF & hash[i])));
				} else {
					hexString.append(Integer.toHexString(0xFF & hash[i]));
				}
			}
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}

		return hexString.toString();
	}

	private static Map getReqValues(HttpServletRequest request) {
		Map<String, String> vmap = new HashMap<String, String>();
		Enumeration<String> e = request.getParameterNames();
		while (e.hasMoreElements()) {
			String name = e.nextElement();
			String value1 = request.getParameter(name);
			String value = value1;
			try {
				value = new String(value1.getBytes("ISO8859-1"), "UTF-8");
			} catch (UnsupportedEncodingException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			vmap.put(name, value);
		}
		  vmap.put("sourceip", request.getRemoteAddr());
		return vmap;
	}

	private Object getLockObject(long rid) {
		Object lock = lockMap.get(rid);
		if (lock == null) {
			Object o = new LockObject(rid, System.currentTimeMillis());
			lockMap.put(rid, o);
			return o;
		}
		return lock;
	}

	class LockObject {
		long rid;
		long time;

		public LockObject(long rid, long time) {
			this.rid = rid;
			this.time = time;
		}

		public long getRid() {
			return rid;
		}

		public long getTime() {
			return time;
		}

	}

	private static String getError(String code, String msg,
			HttpServletResponse response) throws Exception {
		Map<String, Object> m = new HashMap<String, Object>();
		m.put(returnCode, code);
		m.put(returnMessage, msg);
		m.put(result, new ArrayList());
		String res = new GsonBuilder().create().toJson(m);
		response.setCharacterEncoding("GBK");
		response.getWriter().print(res);
		return null;
	}

	public static String serviceId = "CLWID";
	public static String custId = "custId";
	public static String signMsg = "signmsg";
	public static String sendTime = "ts";
	public static String returnCode = "returnCode";
	public static String returnMessage = "returnMessage";
	public static String result = "result";
	public static Map<Long, Object> lockMap = java.util.Collections
			.synchronizedMap(new HashMap<Long, Object>());

	@Resource
	private OracleSequenceMaxValueIncrementer seqDispGenerator;

	@Resource
	private TrequestService trequestService;

	public static void main(String args[]) {
		try {
			byte a[] = "safdasfasf".getBytes("GBK");
			String res = new String(a, "GBK");
			System.out.println(res);
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}